package com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.sap;

import com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.sap.dto.OrderDetailConfirmQuantityResp;
import com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.sap.innerService.FulfillmentOrderService;
import com.alibaba.cirtus.ots.application.adapter.ability.facade.serviceV2.sap.innerService.SalesOrderService;
import com.alibaba.cirtus.ots.application.adapter.ability.model.api.BcFulfillmentOrderDetailSearchResponse;
import com.alibaba.cirtus.ots.application.adapter.ability.model.api.BcFulfillmentOrderDetailSearchResponseResult;
import com.alibaba.cirtus.ots.application.adapter.ability.model.response.OrderAndDetailResponse;
import com.alibaba.cirtus.ots.application.adapter.ability.model.response.SalesOrderConfirmQuantitySapEventMessage;
import com.alibaba.cirtus.ots.application.adapter.ability.utils.HttpTokenUtil;
import com.alibaba.cirtus.ots.application.adapter.ability.utils.HttpUtils;
import com.alibaba.cirtus.ots.application.adapter.ability.utils.RequestUtils;
import com.alibaba.citrus.ots.common.annotation.ProcessInvoker;
import com.alibaba.citrus.ots.common.exception.FunctionException;
import com.alibaba.citrus.ots.common.util.DateUtils;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.middleware.cache.runtime.service.EpochCacheService;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.epoch.app.bcots.contants.BcOtsConstants;
import com.epoch.app.crb2btradecenter.api.orderquery.dto.QueryByOrderBizIdRequest;
import com.epoch.app.crb2btradecenter.api.orderquery.service.OrderQueryService;
import com.epoch.app.crb2btradecenter.domain.order.model.Order;
import com.epoch.app.crb2btradecenter.domain.order.service.OrderService;
import com.epoch.app.crb2btradecenter.domain.orderline.service.OrderLineService;
import com.epoch.app.crb2btradecenter.sdo.OrderSDO;
import com.epoch.app.mnordercenter.orderbaserequest.OrderAtomUpdateRequest;
import com.epoch.app.mnordercenter.orderbaserequest.OrderLineAtomUpdateRequest;
import com.epoch.app.otsapplicationadaptersdk.contants.OtsApplicationAdapterSdkConstants;
import com.epoch.app.otsapplicationadaptersdk.model.dto.FulfillmentDetailGetRes;
import com.epoch.app.otsapplicationadaptersdk.model.dto.FulfillmentGetRes;
import com.epoch.app.otsapplicationadaptersdk.model.dto.RestAllocateOrderConfirmRequest;
import com.epoch.app.otsapplicationadaptersdk.sap.*;
import com.epoch.app.otsapplicationadaptersdk.sapv2.mnsapifsomodify.dto.*;
import com.epoch.app.otsapplicationadaptersdk.sapv2.mnsapifsomodify.service.MnSapIfSOModifyService;
import com.epoch.app.otsapplicationadaptersdk.service.ApiAdapterService;
import com.epoch.app.otsapplicationadaptersdk.service.OtsApplicationAdapterSdkService;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.AtomicDouble;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author zhoubc
 * @description: 平台请求sap同步销售单修改
 * @date 2023/5/16 20:57
 */
@Component
@Primary
public class MnSapIfSOModifyServiceImpl implements MnSapIfSOModifyService {
    private final Log log = Log.getLogger(MnSapIfSOModifyServiceImpl.class);

    private final String MAIN_NOTE_CODE = "Z002";
    private final String LINE_NOTE_CODE = "Z013";
    private final String LANGU_CODE = "1";
    private final String ETENR_CODE = "0001";
    @Resource
    private ApiAdapterService apiAdapterService;
    @Resource
    private OrderQueryService orderQueryService;
    @Resource
    private EpochCacheService epochCacheService;
    @Resource
    private OtsApplicationAdapterSdkService otsApplicationAdapterSdkService;
    @Resource
    private OrderService orderService;
    @Resource
    private OrderLineService orderLineService;
    @Resource
    private SalesOrderService salesOrderService;
    @Resource
    private FulfillmentOrderService fulfillmentOrderService;
    @Autowired
    private HttpUtils httpUtils;
    @Override
    @ProcessInvoker(remark = "MnSapIfSOModifyService", errorCode = "OTS-02-007-00-16-999", printLog = true)
    public Object doHandler(DoHandlerRequest doHandlerRequest) {
        List<OrderAtomUpdateRequest> batchUpdateRequest = JSON.parseArray(doHandlerRequest.getMessage(), OrderAtomUpdateRequest.class);
        log.info("MnSapIfSOModifyServiceImpl doHandler batchUpdateRequest={}",JSON.toJSONString(batchUpdateRequest));
        if (batchUpdateRequest == null || CollectionUtils.isEmpty(batchUpdateRequest)){
            log.info("MnSapIfSOModifyServiceImpl_doHandler_updateRequest_is_null");
            return Result.success(true);
        }

        OrderAtomUpdateRequest updateRequest = batchUpdateRequest.get(0);
        QueryByOrderBizIdRequest queryByOrderBizIdRequest = new QueryByOrderBizIdRequest();
        queryByOrderBizIdRequest.setOrderBizId(updateRequest.getSalesOrderNo());
        OrderSDO saleOrderSDO = orderQueryService.queryByOrderBizId(queryByOrderBizIdRequest);
        log.info("MnSapIfSOModifyServiceImpl doHandler saleOrderSDO={}",JSON.toJSONString(saleOrderSDO));
        if(Objects.nonNull(saleOrderSDO)){
            String[] businessOrderTypes = {"ZBV","ZFD2","ZOR2","ZORO","ZORA","ZFD3","ZCH2","ZRE2","ZRE3","ZRE5"};
            List<String> businessList = Arrays.asList(businessOrderTypes);
            boolean isB_b = businessList.contains(saleOrderSDO.getOrderType()); //orderType
            if (isB_b){
                updateRequest.setExpectedDeliveryDate(null);
            }
        }
        MnSapIfSOModifyRequest request = new MnSapIfSOModifyRequest();
        request.setMessageHeader(RequestUtils.generateMessageHeader("SaleOrder_CHANGE","ZT_OMC","ECC"));
        MnSapIfSOModifyHeadSDO headSDO = new MnSapIfSOModifyHeadSDO();
        List<MnSapIfSOModifyItemSDO> item2_item = new ArrayList<>();
        List<MnSapIfSOModifyScheduleSDO> item3_schedule = new ArrayList<>();
        List<MnSapIfSOModifyTextSDO> item4_text = new ArrayList<>();
        headSDO.setVBELN(updateRequest.getOutOrderNo());
        headSDO.setVSBED(updateRequest.getDeliveryMethodCode());
        headSDO.setAUGRU(updateRequest.getOrderReasonCode());
        headSDO.setKOSTL(updateRequest.getCostCenterCode());
        //headSDO.setBSTKD(updateRequest.getCustomerPurchaseOrderNo());
        MnSapIfSOModifyTextSDO textSDO = new MnSapIfSOModifyTextSDO();
        textSDO.setVBELN(updateRequest.getOutOrderNo());
        textSDO.setTEXTID(MAIN_NOTE_CODE);
        textSDO.setLANGU(LANGU_CODE);
        textSDO.setTEXTLINE(updateRequest.getNote());
        item4_text.add(textSDO);
        String maxLineNo = getMaxSapLineNo(updateRequest.getOrderDetails());
        Integer currentLineNo =0;
        for (OrderLineAtomUpdateRequest updateLineRequest : updateRequest.getOrderDetails()) {
            MnSapIfSOModifyItemSDO itemSDO = new MnSapIfSOModifyItemSDO();
            itemSDO.setVBELN(updateRequest.getOutOrderNo());
            itemSDO.setPOSNR(updateLineRequest.getOutLineNum());
//            itemSDO.setABGRU(updateLineRequest.getCloseCode());
            itemSDO.setWERKS(updateLineRequest.getDeliveryFactoryCode());
            itemSDO.setLGORT(updateLineRequest.getStockCode());
            //itemSDO.setZFSCRQ(updateLineRequest.getProductionDate());
            itemSDO.setHEDI01(updateLineRequest.getProductQuantity());
            //新增行的情况
            if(StringUtils.isEmpty(updateLineRequest.getOutLineNum())){
                currentLineNo = getCurrentSapLineNo(maxLineNo,currentLineNo);
                itemSDO.setPOSNR(String.valueOf(currentLineNo));
                //物料编码
                itemSDO.setHEDI02(updateLineRequest.getProductCode());
                //单位编码
                itemSDO.setHEDI03(updateLineRequest.getSalesUnitCode());
                //生产日期
                itemSDO.setZFSCRQ(updateLineRequest.getProductionDate().replaceAll("-",""));
                //交货日期
                itemSDO.setHEDI04(updateRequest.getExpectedDeliveryDate().replaceAll("-",""));

            }

            item2_item.add(itemSDO);//不可为空item2_item，影响行备注sap修改是否成功
            if(StringUtils.isNotEmpty(updateLineRequest.getOutLineNum()) && StringUtils.isNotBlank(updateRequest.getExpectedDeliveryDate())){
                MnSapIfSOModifyScheduleSDO scheduleSDO = new MnSapIfSOModifyScheduleSDO();
                scheduleSDO.setVBELN(updateRequest.getOutOrderNo());
                scheduleSDO.setPOSNR(updateLineRequest.getOutLineNum());
                scheduleSDO.setETENR(ETENR_CODE);
                scheduleSDO.setEDATU(updateRequest.getExpectedDeliveryDate().replaceAll("-",""));
                item3_schedule.add(scheduleSDO);
            }
            if(StringUtils.isNotEmpty(updateLineRequest.getOutLineNum())){
                MnSapIfSOModifyTextSDO lineTextSDO = new MnSapIfSOModifyTextSDO();
                lineTextSDO.setVBELN(updateRequest.getOutOrderNo());
                lineTextSDO.setPOSNR(updateLineRequest.getOutLineNum());
                lineTextSDO.setTEXTID(LINE_NOTE_CODE);
                lineTextSDO.setLANGU(LANGU_CODE);
                lineTextSDO.setTEXTLINE(updateLineRequest.getNote());
                item4_text.add(lineTextSDO);
            }

        }
        headSDO.setItem2_item(item2_item);
        headSDO.setItem3_schedule(item3_schedule);
        headSDO.setItem4_text(item4_text);
        request.setItem1_head(headSDO);
        log.info("reqSapSalesOrderModify:{}", JSONObject.toJSONString(request));
        //请求写入redis,key=out_order_id 用于sap回调回来，失败场景重试机制
        try {
            boolean put = epochCacheService.put(BcOtsConstants.REDIS_CACHE_CODE(), updateRequest.getOutOrderNo(), JSONObject.toJSONString(request), 30L, TimeUnit.MINUTES);
            log.info("MnSapIfSOModifyServiceImpl doHandler save redis result={}",put);
        }catch (Exception e){
            log.error("MnSapIfSOModifyServiceImpl doHandler error",e);
        }

        return apiAdapterService.reqSapSalesOrderModify(request);
    }

    private Integer getCurrentSapLineNo(String maxLineNo,Integer currentLineNo) {
        //设置行号
        if(!Objects.equals(0,currentLineNo)){
            //存在多行新增的情况
            currentLineNo += 10;
        }else {
            //新增的第一行
            currentLineNo = Integer.parseInt(maxLineNo) + 10;
        }
        return currentLineNo;
    }

    @Override
    @ProcessInvoker(remark = "MnSapIfSOModifyService", errorCode = "OTS-02-007-00-16-999", printLog = true)
    public Object doHandlerRetry(DoHandlerRetryRequest doHandlerRetryRequest) {
        log.info("MnSapIfSOModifyServiceImpl doHandlerRetry doHandlerRetryRequest={}",JSON.toJSONString(doHandlerRetryRequest));
        String message = doHandlerRetryRequest.getMessage();
        String json = epochCacheService.get("REDIS-CASHE", message);
        log.info("MnSapIfSOModifyServiceImpl doHandlerRetry json={}",json);
        MnSapIfSOModifyRequest request = JSON.parseObject(json, MnSapIfSOModifyRequest.class);
        log.info("MnSapIfSOModifyServiceImpl doHandlerRetry request={}",JSON.toJSONString(request));
        return apiAdapterService.reqSapSalesOrderModify(request);
    }

    @Override
    @ProcessInvoker(remark = "MnSapIfSOModifyService", errorCode = "OTS-02-007-00-16-999", printLog = true)
    public Result<Void> confirmQuantitySync(ConfirmQuantitySyncRequest confirmQuantitySyncRequest) {
        if(Objects.isNull(confirmQuantitySyncRequest) || StringUtils.isEmpty(confirmQuantitySyncRequest.getMessage())){
            return Result.success(null);
        }
        log.info("MnSapIfSOModifyServiceImpl confirmQuantitySync confirmQuantitySyncRequest = {}",JSON.toJSONString(confirmQuantitySyncRequest));

        String message = confirmQuantitySyncRequest.getMessage();
        JSONObject json = JSON.parseObject(message);
        JSONObject data = json.getJSONObject("data");
        String orderNo = data.getString("orderNo");
        Integer businessType = data.getInteger("businessType");
        String fulfillmentOrderNo = data.getString("fulfillmentOrderNo");
        String redisKey = String.format("%s-%s", "confirmQuantitySync", orderNo);
        //需要加分布式锁，避免并发
        boolean setnx = epochCacheService.setnx(BcOtsConstants.REDIS_CACHE_CODE(), redisKey, "1");
        if(setnx){
            //过期时间，避免一直锁住，如果30分钟SAP都没有回来，那是他们的问题了
            epochCacheService.expire(BcOtsConstants.REDIS_CACHE_CODE(),redisKey,30,TimeUnit.MINUTES);
            log.info("redisKey {} obtain lock ready handle",redisKey);
            //查询当前履约单

            OrderAndDetailResponse orderAndDetail = salesOrderService.getOrderAndDetailByOrderBizId(orderNo,businessType);
            if(Objects.isNull(orderAndDetail)){
                unlock(redisKey);
                return Result.fail("9999", "同步确认数量查询订单失败");
            }
            //过滤掉不走OTW的F-B订单，采用履约方式判断 外仓发货大仓收货，外仓发货外仓收货 不进行047处理
            List<String> filterFulfillmentTypes = Arrays.asList("120", "130");
            if(filterFulfillmentTypes.contains(orderAndDetail.getOrder().getFulfillmentTypeCode())){
                unlock(redisKey);
                return Result.success(null);
            }
            String[] orderType = {"ZFOC","ZOR","ZOR8"};
            List<String> orderTypeList = Arrays.asList(orderType);
            Order order = orderAndDetail.getOrder();
            if(!("200".equals(order.getOrderCategoryCode()) && orderTypeList.contains(order.getOrderType()))){
                log.info(" if category {} not is F-B ,then not handle ",orderAndDetail.getOrder().getOrderCategoryCode());
                unlock(redisKey);
                return Result.success(null);
            }
            if (StringUtils.isEmpty(order.getOuterOrderId())){
                log.info(" order_biz_id {} not receive out_order_id ",order.getOrderBizId());
                unlock(redisKey);
                return Result.fail("9999", "外部单号为空");
            }
            //如果履约单不是空，查询履约单，如果是空，表示这个履约单是没有新建的，不属于内部修改场景
            if(StringUtils.isNotEmpty(fulfillmentOrderNo)){
                List<BcFulfillmentOrderDetailSearchResponse> forwardDirectionFulfillment = getForwardDirectionFulfillment(order.getOrderBizId(), fulfillmentOrderNo);
                log.info("MnSapIfSOModifyServiceImpl confirmQuantitySync forwardDirectionFulfillment={}",JSON.toJSONString(forwardDirectionFulfillment));
                Optional<BcFulfillmentOrderDetailSearchResponse> any = forwardDirectionFulfillment.stream().filter(it -> {
                    String features = it.getFeatures();
                    JSONObject featureJson = JSON.parseObject(features);
                    return featureJson.containsKey("updateFlag") && featureJson.getBoolean("updateFlag");
                }).findAny();
                log.info("MnSapIfSOModifyServiceImpl confirmQuantitySync any={}",any.isPresent());
                if (any.isPresent()){
                    unlock(redisKey);
                    return Result.success(null);
                }
            }

            //查询订单明细的确认数量
            List<com.epoch.app.crb2btradecenter.domain.orderline.model.OrderLine> orderLineList = orderAndDetail.getOrderLineList();
            for (int i = 0; i < orderLineList.size(); i++) {
                com.epoch.app.crb2btradecenter.domain.orderline.model.OrderLine orderLine = orderLineList.get(i);
                if (Objects.isNull(orderLine.getConfirmQuantity())){
                    unlock(redisKey);
                    return Result.fail("9999", "销售订单确认数量为空");
                }
            }
            //如果订单关闭了，就不需要调用047了
            Map<String, Integer> confirmQuantityMap = orderLineList.stream().filter(it->it.getClosed()==0).collect(Collectors.groupingBy(com.epoch.app.crb2btradecenter.domain.orderline.model.OrderLine::getSerialNumber, Collectors.summingInt(com.epoch.app.crb2btradecenter.domain.orderline.model.OrderLine::getConfirmQuantity)));
            log.info("MnSapIfSOModifyServiceImpl confirmQuantitySync confirmQuantityMap={}",JSON.toJSONString(confirmQuantityMap));
            //差异数据
            Map<String,Integer> diffMap = new HashMap<>();
            //订单维度查询履约单
            List<BcFulfillmentOrderDetailSearchResponse> orderLine = getEffectiveFulfillment(orderAndDetail.getOrder().getOrderBizId(),null);
            //过滤掉由于中台修改，关闭和新建的履约单
            log.info("MnSapIfSOModifyServiceImpl confirmQuantitySync orderLine={}",JSON.toJSONString(orderLine));
            //ket＝销售订单明细行号　value = 履约数量，是一个累计的结果
            Map<String, Integer> refOrderLineNoMap = orderLine.stream()
                    .collect(Collectors.groupingBy(BcFulfillmentOrderDetailSearchResponse::getRefOrderLineNo, Collectors.summingInt(it->Double.valueOf(it.getQuantity()).intValue())));
            log.info("MnSapIfSOModifyServiceImpl confirmQuantitySync refOrderLineNoMap={}",JSON.toJSONString(refOrderLineNoMap));
            refOrderLineNoMap.forEach((key,value)->{
                String lineNo = key;
                //履约数量
                int quantity = value;
                if(confirmQuantityMap.containsKey(lineNo)){
                    Integer confirmQuantity = confirmQuantityMap.get(lineNo);
                    int diff = quantity - confirmQuantity  ;
                    if(diff!=0){
                        diffMap.put(lineNo,diff);
                    }
                }else{
                    log.error("MnSapIfSOModifyServiceImpl lineNo={} not find lineNo in confirmQuantity",lineNo);
                }
            });
            //如果履约单被过滤掉7的，就需要按照订单维度计算差值，履约单数量=0
            confirmQuantityMap.forEach((key,value)->{
                if(!refOrderLineNoMap.containsKey(key)){
                    int diff = 0-value;
                    if(diff!=0){
                        diffMap.put(key,diff);
                    }
                }
            });
            log.info("MnSapIfSOModifyServiceImpl confirmQuantitySync diffMap={}",JSON.toJSONString(diffMap));
            if(!diffMap.isEmpty()){
                ifExistDiffSendToSap(diffMap, order,refOrderLineNoMap,confirmQuantityMap,fulfillmentOrderNo);
            }else {
                //如果没有差异，需要释放锁
                unlock(redisKey);
            }
        }else{
            log.error("MnSapIfSOModifyServiceImpl orderBizId = {} confirm quantity is sync",redisKey);
            return Result.fail("9999", "确认数量正在同步中");
        }
        return Result.success(null);
    }
    private void unlock(String redisKey) {
        String s = epochCacheService.get(BcOtsConstants.REDIS_CACHE_CODE(), redisKey);
        log.info("MnSapIfSOModifyServiceImpl unlock key={},is exist={}",redisKey,s);
        if (s == null){
            return;
        }
        boolean remove = epochCacheService.remove(BcOtsConstants.REDIS_CACHE_CODE(), Lists.newArrayList(redisKey));
        if (remove){
            log.info("MnSapIfSOModifyServiceImpl ThreadId="+Thread.currentThread().getId()+"|SaleRedis key="+redisKey+"|unlock");
        }
    }

    private List<BcFulfillmentOrderDetailSearchResponse> getEffectiveFulfillment(String orderBizId,String fulfillmentOrderNo){
        String[] filterStatus = {"1","7"};
        List<String> filterList = Arrays.asList(filterStatus);
        List<BcFulfillmentOrderDetailSearchResponse> convert = convert(orderBizId, fulfillmentOrderNo);
        log.info("MnSapIfSOModifyServiceImpl getEffectiveFulfillment convert={}",JSON.toJSONString(convert));
        return convert.stream().filter(it->!filterList.contains(it.getDetailStatus()) && "1".equals(it.getFulfillmentDirectionType())).collect(Collectors.toList());
    }
    private List<BcFulfillmentOrderDetailSearchResponse> getForwardDirectionFulfillment(String orderBizId,String fulfillmentOrderNo){
        List<BcFulfillmentOrderDetailSearchResponse> convert = convert(orderBizId, fulfillmentOrderNo);
        log.info("MnSapIfSOModifyServiceImpl getEffectiveFulfillment forwardDirection={}",JSON.toJSONString(convert));
        return convert.stream().filter(it->"1".equals(it.getFulfillmentDirectionType())).collect(Collectors.toList());
    }
    private List<BcFulfillmentOrderDetailSearchResponse> convert(String orderBizId,String fulfillmentOrderNo){
        List<BcFulfillmentOrderDetailSearchResponse> list = new ArrayList<>();
        List<FulfillmentGetRes> fulfillmentGetRes = searchFulfillmentOrder(orderBizId, fulfillmentOrderNo);
        log.info("MnSapIfSOModifyServiceImpl convert fulfillmentGetRes={}",JSON.toJSONString(fulfillmentGetRes));
        if(CollectionUtils.isEmpty(fulfillmentGetRes)){
            return  list;
        }
        fulfillmentGetRes.stream().forEach(it->{
            List<FulfillmentDetailGetRes> detailList = it.getDetailList();
            detailList.stream().forEach(item->{
                BcFulfillmentOrderDetailSearchResponse response = new BcFulfillmentOrderDetailSearchResponse();
                BeanUtils.copyProperties(item,response);
                response.setDetailStatus(item.getStatus());
                response.setMainStatus(it.getStatus());
                response.setFulfillmentDirectionType(String.valueOf(it.getFulfillmentDirectionType()));
                response.setFeatures(it.getBizFeatures());
                list.add(response);
            });
        });
        return list;
    }
    private List<FulfillmentGetRes> searchFulfillmentOrder(String orderBizId,String fulfillmentOrderNo){
        log.info("MnSapIfSOModifyServiceImpl searchFulfillmentOrder orderBizId={},fulfillmentOrderNo={}",orderBizId,fulfillmentOrderNo);
        if(StringUtils.isEmpty(orderBizId)&&StringUtils.isEmpty(fulfillmentOrderNo)){
            return new ArrayList<>();
        }
        List<FulfillmentGetRes> result = new ArrayList<>();
        try{
            SearchFulfillmentOrderInnerRequest request = new SearchFulfillmentOrderInnerRequest();
            request.setRefOrderNo(orderBizId);
            int pageSize =100;
            request.setPage(1);
            request.setSize(pageSize);
            request.setFulfillmentOrderNo(fulfillmentOrderNo);
            log.info("MnSapIfSOModifyServiceImpl searchFulfillmentOrder request={}",JSON.toJSONString(request));

            Result<List<FulfillmentGetRes>> fulfillmentGetResResult = searchFulfillmentOrderInner(request);
            log.info("MnSapIfSOModifyServiceImpl searchFulfillmentOrder first fulfillmentGetResResult={}",JSON.toJSONString(fulfillmentGetResResult));
            int total = fulfillmentGetResResult.getTotal();
            result = fulfillmentGetResResult.getResult();
            int size = result.size();
            if (total>pageSize){
                int remain = total-size;
                int page  =  remain %pageSize ==0? (remain/pageSize):(remain/pageSize+1);
                for (int i = 1; i <= page; i++) {
                    request.setPage(i+1);
                    log.info("MnSapIfSOModifyServiceImpl searchFulfillmentOrder paging request={}",JSON.toJSONString(request));
                    fulfillmentGetResResult = searchFulfillmentOrderInner(request);

                    log.info("MnSapIfSOModifyServiceImpl searchFulfillmentOrder paging fulfillmentGetResResult={}",JSON.toJSONString(fulfillmentGetResResult));
                    if(Objects.isNull(fulfillmentGetResResult) || CollectionUtils.isEmpty(fulfillmentGetResResult.getResult())){
                        break;
                    }
                    result.addAll(fulfillmentGetResResult.getResult());
                }
            }
        }catch (Exception e){
            log.error("MnSapIfSOModifyServiceImpl searchFulfillmentOrder error",e);
        }

        return result;
    }
    /**
     * 如果订单明细的履约数量和确认数量存在差异，需要调用047接口同步给sap
     * @param
     * @param diffMap 订单明细的确定数量map
     * @param refOrderLineNoMap
     * @param confirmQuantityMap
     * @param fulfillmentOrderNo
     */
    private void ifExistDiffSendToSap(Map<String, Integer> diffMap, Order order, Map<String, Integer> refOrderLineNoMap, Map<String, Integer> confirmQuantityMap, String fulfillmentOrderNo) {
        //构建参数发送047接口 TODO
        RestAllocateOrderConfirmRequest request = new RestAllocateOrderConfirmRequest();
        JSONObject messageHeaderSDO = RequestUtils.generateMessageHeader("SI_ZT_AllocateConfirm_Asyn_OUT","YWZT","ECC","yyyyMMddHHmmss");
        request.setMessageHeader(messageHeaderSDO);
        request.setDocType("SO");
        request.setExt("");
        request.setExt1("");
        request.setExt2("");
        List<JSONObject> list = new ArrayList<>();
        diffMap.forEach((key,value)->{
            JSONObject item = new JSONObject();
            item.put("diffConfirmQuantity",value);
            item.put("posnr",key);
            item.put("vbeln",order.getOuterOrderId());
            item.put("edatu","");
            list.add(item);
        });
        log.info("MnSapIfSOModifyServiceImpl list={} ",JSON.toJSONString(list));
        if(CollectionUtils.isNotEmpty(list)){
            request.setItems(list);
            log.info("MnSapIfSOModifyServiceImpl current order line confirm quantity={},delivery quantity={}",JSON.toJSONString(confirmQuantityMap),JSON.toJSONString(refOrderLineNoMap));
            log.info("MnSapIfSOModifyServiceImpl ifExistDiffSendToSap request ={},",JSON.toJSONString(request));
            Object result = otsApplicationAdapterSdkService.restAllocateOrderConfirm(request);
            log.info("MnSapIfSOModifyServiceImpl ifExistDiffSendToSap result ={},",JSON.toJSONString(result));
            toBcLink(request, order.getOrderBizId(), order.getBusinessType(),fulfillmentOrderNo);
        }
    }

    private void toBcLink(RestAllocateOrderConfirmRequest request, String orderBizId, Integer businessType,String fulfillmentOrderNo) {
        log.info("MnSapIfSOModifyServiceImpl toBcLink param:{}", JSON.toJSONString(request));
        try {
            Map<String, String> header = HttpTokenUtil.generateHttpHeader();
            String urlPrefix = OtsApplicationAdapterSdkConstants.urlPrefix();
            String url = urlPrefix + "api/mn_bc_link/saveTaskLog";

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("apiCode", "MN-SAP-IF-047");
            jsonObject.put("apiDesc", "中台确认数量同步SAP");
            jsonObject.put("callback", false);
            jsonObject.put("errorCode", "");
            jsonObject.put("errorMessage", "");
            jsonObject.put("executeStatus", 1);
            jsonObject.put("interfacePath", "MnSapIfSOModify$confirmQuantitySync");
            jsonObject.put("key", orderBizId);
            jsonObject.put("messageId", JSON.parseObject(JSON.toJSONString(request.getMessageHeader())).getString("messageId"));

            JSONObject retryParam = new JSONObject();
            JSONObject data = new JSONObject();
            data.put("businessType",businessType);
            data.put("orderNo",orderBizId);
            data.put("fulfillmentOrderNo",fulfillmentOrderNo);
            JSONObject message = new JSONObject();
            message.put("data", data);
            retryParam.put("message", message);

            jsonObject.put("retryParam", retryParam.toJSONString());
            jsonObject.put("requestInfo", JSON.toJSONString(request));
            jsonObject.put("sourceClient", "ZT");
            jsonObject.put("targetClient", "SAP");
            jsonObject.put("bizOrderId", orderBizId);

            log.info("MnSapIfSOModifyServiceImpl toBcLink call bc link interface,url:{}, header:{}, " + "param:{}", url,
                header, jsonObject);
            ResponseEntity<com.alibaba.cirtus.ots.application.adapter.ability.model.Result> post = httpUtils.post(url, header, jsonObject,
                com.alibaba.cirtus.ots.application.adapter.ability.model.Result.class);
            com.alibaba.cirtus.ots.application.adapter.ability.model.Result result = post.getBody();
            log.info("MnSapIfSOModifyServiceImpl toBcLink result:{}", JSON.toJSONString(result));
        } catch (Exception e) {
            log.error("MnSapIfSOModifyServiceImpl toBcLink error", e);
        }
    }
    private String getMaxSapLineNo(List<OrderLineAtomUpdateRequest> list){
        Optional<String> first = list.stream().filter(it -> StringUtils.isNotEmpty(it.getOutLineNum()))
                .sorted(Comparator.comparing(OrderLineAtomUpdateRequest::getOutLineNum).reversed()).map(OrderLineAtomUpdateRequest::getOutLineNum).findFirst();
        String maxLineNo = "0";
        if(first.isPresent()){
            //当前明细最大的行号
            maxLineNo = first.get();
        }
        return maxLineNo;
    }

    @Override
    public Object confirmQuantityFirstSync(ConfirmQuantityFirstSyncRequest confirmQuantityFirstSyncRequest) {
        if(Objects.isNull(confirmQuantityFirstSyncRequest)){
            return Result.success(true);
        }
        log.info("MnSapIfSOModifyServiceImpl confirmQuantityFirstSync confirmQuantityFirstSyncRequest = {}",JSON.toJSONString(confirmQuantityFirstSyncRequest));

        String message = confirmQuantityFirstSyncRequest.getMessage();
        SalesOrderConfirmQuantitySapEventMessage eventMessage = JSON.parseObject(message, SalesOrderConfirmQuantitySapEventMessage.class);
        log.info("MnSapIfSOModifyServiceImpl eventMessage ={}",JSON.toJSONString(eventMessage));
        String orderNo = eventMessage.getOrderNo();
        Integer businessType = eventMessage.getBusinessType();
        MnSapIf013SDO sdo = eventMessage.getSdo();
        Long firstTime = eventMessage.getFirstTime();
        OrderDetailConfirmQuantityResp orderDetailConfirmQuantityMap = getOrderDetailConfirmQuantity(orderNo,businessType);
        log.info("MnSapIfSOModifyServiceImpl orderDetailConfirmQuantityMap={}",JSON.toJSONString(orderDetailConfirmQuantityMap));
        if(System.currentTimeMillis() - firstTime > 5*60*1000l){
            doSendToSap(orderNo,sdo,orderDetailConfirmQuantityMap,businessType);
            return Result.success(true);
        }
        //如果履约单没有生成，就不做处理了
        if(orderDetailConfirmQuantityMap.getOrderDetailConfirmQuantityMap().isEmpty()){
            log.warn("orderNo= {} fulfillment status not three",orderNo);
            throw  new FunctionException("履约单还没有生成，重试");
        }
        doSendToSap(orderNo,sdo,orderDetailConfirmQuantityMap,businessType);
        return Result.success(true);
    }
    private void doSendToSap(String orderNo, MnSapIf013SDO sdo, OrderDetailConfirmQuantityResp orderDetailConfirmQuantityMap, Integer businessType){
        //履约单是否和销售确认数量有差异
        String redisKey = String.format("%s-%s", "confirmQuantitySync", orderNo);

        boolean setnx = epochCacheService.setnx(BcOtsConstants.REDIS_CACHE_CODE(), redisKey, "1");
        if(setnx){
            //过期时间，避免一直锁住，如果30分钟SAP都没有回来，那是他们的问题了
            epochCacheService.expire(BcOtsConstants.REDIS_CACHE_CODE(),redisKey,30,TimeUnit.MINUTES);
            log.info("MnSapIfSOModifyServiceImpl ifExistDiffSendToSap redisKey {} obtain lock ready handle",redisKey);
            ifExistDiffSendToSap(orderDetailConfirmQuantityMap.getOrderDetailConfirmQuantityMap(), sdo, orderDetailConfirmQuantityMap.getFulfillmentOrderNo(),orderNo,businessType,redisKey);
        }else {
            log.info("MnSapIfSOModifyServiceImpl confirmQuantityFirstSync ifExistDiffSendToSap redisKey = {} not obtain lock",orderNo);
        }
    }


    private void ifExistDiffSendToSap(Map<String, String> orderDetailConfirmQuantityMap, MnSapIf013SDO sdo, String fulfillmentOrderNo,String orderNo ,Integer businessType,String redisKey) {
        log.info("MnSapIfSOModifyServiceImpl ifExistDiffSendToSap orderDetailConfirmQuantityMap={}",JSON.toJSONString(orderDetailConfirmQuantityMap));
        List<MnSapIf013Detail> items = sdo.getItems();
        Map<String, String> scItemIdMap = new HashMap<>();
        items.forEach(it-> scItemIdMap.put(it.getItem_key().replaceAll("^0*",""),it.getConfirmQuantity()));
        Map<String,String> lineNoMap = new HashMap<>();
        scItemIdMap.forEach((key,value)->{
            if(orderDetailConfirmQuantityMap.containsKey(key)){
                String quantity = orderDetailConfirmQuantityMap.get(key);
                //如果sap发过来的确认数量是null,当作0.0处理
                String valueTemp  = StringUtils.isNotBlank(value) ? value:"0.0";
                Double diff = Double.parseDouble(quantity)-Double.parseDouble(valueTemp);
                if(diff!=0.0D){
                    lineNoMap.put(key,String.valueOf(diff));
                }
            }else{
                //如果没有履约数量就默认是0.0
                String valueTemp  = StringUtils.isNotBlank(value) ? value:"0.0";
                Double diff = Double.parseDouble("0.0")-Double.parseDouble(valueTemp);
                if(diff!=0.0D){
                    lineNoMap.put(key,String.valueOf(diff));
                }
            }
        });
        //构建参数发送047接口 TODO
        if(CollectionUtils.isNotEmpty(items)){
            RestAllocateOrderConfirmRequest request = new RestAllocateOrderConfirmRequest();
            JSONObject messageHeaderSDO = RequestUtils.generateMessageHeader("SI_ZT_AllocateConfirm_Asyn_OUT","YWZT","ECC","yyyyMMddHHmmss");
            request.setMessageHeader(messageHeaderSDO);
            request.setDocType("SO");
            request.setExt("");
            request.setExt1("");
            request.setExt2("");
            List<JSONObject> list = new ArrayList<>();
            items.stream().forEach(it->{
                JSONObject item = new JSONObject();
                String itemKey = it.getItem_key().replaceAll("^0*", "");
                if(lineNoMap.containsKey(itemKey)){
                    String diff = lineNoMap.get(itemKey);
                    item.put("diffConfirmQuantity",diff);
                    item.put("posnr",itemKey);
                    item.put("vbeln",sdo.getHeaderKey());
                    item.put("edatu","");
                    list.add(item);
                }else{
                    log.error("order detail lineNo={} not exist diff ",itemKey);
                }
            });
            if(CollectionUtils.isNotEmpty(list)){
                request.setItems(list);
                log.info("MnSapIfSOModifyServiceImpl sap confirm quantity={},delivery quantity={}",JSON.toJSONString(scItemIdMap),JSON.toJSONString(orderDetailConfirmQuantityMap));
                log.info("ifExistDiffSendToSap request ={},",JSON.toJSONString(request));
                Object result = otsApplicationAdapterSdkService.restAllocateOrderConfirm(request);
                log.info("ifExistDiffSendToSap result ={},",JSON.toJSONString(result));
                toBcLink(request, orderNo,fulfillmentOrderNo,businessType);
            }else{
                unlock(redisKey);
            }
        }else {
            unlock(redisKey);
        }
    }
    private void toBcLink(RestAllocateOrderConfirmRequest request, String orderBizId, String fulfillmentOrderNo,Integer businessType) {
        log.info("MnSapIfSOModifyServiceImpl toBcLink param:{}", JSON.toJSONString(request));
        try {
            Map<String, String> header = HttpTokenUtil.generateHttpHeader();
            String urlPrefix = OtsApplicationAdapterSdkConstants.urlPrefix();
            String url = urlPrefix + "api/mn_bc_link/saveTaskLog";

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("apiCode", "MN-SAP-IF-047");
            jsonObject.put("apiDesc", "中台确认数量同步SAP");
            jsonObject.put("callback", false);
            jsonObject.put("errorCode", "");
            jsonObject.put("errorMessage", "");
            jsonObject.put("executeStatus", 1);
            jsonObject.put("interfacePath", "MnSapIfSOModify$confirmQuantitySync");
            jsonObject.put("key", orderBizId);
            jsonObject.put("messageId", JSON.parseObject(JSON.toJSONString(request.getMessageHeader())).getString("messageId"));

            JSONObject retryParam = new JSONObject();
            JSONObject data = new JSONObject();
            data.put("businessType",businessType);
            data.put("orderNo",orderBizId);
            data.put("fulfillmentOrderNo",fulfillmentOrderNo);
            JSONObject message = new JSONObject();
            message.put("data", data);
            retryParam.put("message", message);

            jsonObject.put("retryParam", retryParam.toJSONString());
            jsonObject.put("requestInfo", JSON.toJSONString(request));
            jsonObject.put("sourceClient", "ZT");
            jsonObject.put("targetClient", "SAP");
            jsonObject.put("bizOrderId", orderBizId);

            log.info("MnSapIfSOModifyServiceImpl toBcLink call bc link interface,url:{}, header:{}, " + "param:{}", url,
                    header, jsonObject);
            ResponseEntity<com.alibaba.cirtus.ots.application.adapter.ability.model.Result> post = httpUtils.post(url, header, jsonObject,
                    com.alibaba.cirtus.ots.application.adapter.ability.model.Result.class);
            com.alibaba.cirtus.ots.application.adapter.ability.model.Result result = post.getBody();
            log.info("MnSapIfSOModifyServiceImpl toBcLink result:{}", JSON.toJSONString(result));
        } catch (Exception e) {
            log.error("MnSapIfSOModifyServiceImpl toBcLink error", e);
        }
    }
    /**
     * 获取订单明细的履约数量，用于赋值给订单明细的确定数量
     * @return 订单明细的履约数量
     * @param orderNo 订单数据
     */
    private OrderDetailConfirmQuantityResp getOrderDetailConfirmQuantity(String orderNo,Integer businessType) {
        OrderDetailConfirmQuantityResp resp = new OrderDetailConfirmQuantityResp();
        Map<String,String> map = new HashMap<>();
        Order order = new Order();
        order.setOrderBizId(orderNo);
        order.setBusinessType(businessType);
        List<BcFulfillmentOrderDetailSearchResponse> searchResponse = getEffectiveFulfillment(orderNo,null);
        log.info("MnSapIfSOModifyServiceImpl getOrderDetailConfirmQuantity searchResponse={}",JSON.toJSONString(searchResponse));
        if (CollectionUtils.isEmpty(searchResponse)){
            resp.setOrderDetailConfirmQuantityMap(map);
            return resp;
        }

        Map<String, List<BcFulfillmentOrderDetailSearchResponse>> lineNoMap = searchResponse.stream().collect(Collectors.groupingBy(BcFulfillmentOrderDetailSearchResponse::getRefOrderLineNo));
        lineNoMap.forEach((key,value)->{
            AtomicDouble atomicDouble = new AtomicDouble();
            value.stream().forEach(it->atomicDouble.addAndGet(Double.valueOf(it.getQuantity())));
            map.put(key,atomicDouble.toString());
        });
        resp.setOrderDetailConfirmQuantityMap(map);
        resp.setFulfillmentOrderNo(searchResponse.get(0).getFulfillmentOrderNo());
        return resp;
    }


}
